#include "World.h"
#include "..\base\Engine.h"
#include "..\nodes\Node.h"
#include "..\content\ResManager.h"
#include "..\utils\StringUtils.h"

namespace ds {

// ----------------------------------------------
//
// ----------------------------------------------
World::World() : m_Counter(0) , m_NodeCounter(0) , m_Dirty(true) {
	m_Data = new uint32[ 3 * MAX_LAYERS ];
	uint32* data = m_Data;
	for ( int i = 0; i < 3 * MAX_LAYERS; ++i ) {
		*data = 0;
		++data;
	}
	m_NameMapping = new char[MAX_LAYERS * MAX_NAME_LENGTH];
	for ( uint32 i = 0; i < MAX_NAME_LENGTH*MAX_LAYERS; ++i ) {
		m_NameMapping[i] = 0;
	}	
	m_LayerNodes = new LayerNode[MAX_NODES];
}

// ----------------------------------------------
//
// ----------------------------------------------
World::~World() {
	LOGC(logINFO,"World") << "destructing world";
	LayerNode* ln = m_LayerNodes;
	for ( size_t j = 0 ; j < m_NodeCounter; ++j ) {
		Node* n = ln->node;
		LOGC(logINFO,"World") << "destructing " << n->getName();
		n->onDestroyDevice();
		delete n;
		++ln;
	}		
	delete m_LayerNodes;
	delete m_NameMapping;
	delete m_Data;
}

// ----------------------------------------------
//
// ----------------------------------------------
WorldLayer World::createLayer(WorldMask wm) {	
	assert(m_Counter < 32);
	++m_Counter;
	int idx = (m_Counter-1)*3;
	uint32* data = (m_Data+idx);
	*data = m_Counter;
	++data;
	*data = 1;
	++data;
	*data = wm;
	LOGC(logINFO,"World") << "new layer created " << m_Counter << " with world mask " << wm;
	m_Dirty = true;
    return m_Counter;
}

// ----------------------------------------------
// set name in the name array for the given index
// ----------------------------------------------
void World::setName(WorldLayer layer,const char* name) {
	size_t len = strlen(name);
	if ( len < MAX_NAME_LENGTH ) {
		uint32 idx = layer - 1;
		char* array = (m_NameMapping + idx * MAX_NAME_LENGTH);
		while ( *name ) {
			*array = *name;
			++name;
			++array;
		}
		++array;
		*array = 0;
	}
	else {
		LOGC(logERROR,"World") << "Name of the layer " << name << " is too long - layer will not be created";
	}
}

// ----------------------------------------------
// set name in the name array for the given index
// ----------------------------------------------
void World::getName(WorldLayer layer,char* ret) {
	uint32 idx = layer;	
	char* array = (m_NameMapping+(layer-1)*MAX_NAME_LENGTH);
	uint32 cnt = 0;
	while ( *array ) {
		*ret = *array;
		++ret;
		++array;
		++cnt;
	}
	if ( cnt < MAX_NAME_LENGTH ) {
		for ( int i = cnt ; i < MAX_NAME_LENGTH;++i ) {
			*ret = 0;
			++ret;
		}
	}
}
// ----------------------------------------------
//
// ----------------------------------------------
WorldLayer World::createLayer(const char* name,WorldMask wm) {
	LOGC(logINFO,"World") << "creating layer " << name;
	WorldLayer l = createLayer(wm);
	setName(l,name);
	m_Dirty = true;
	return l;
}

// ----------------------------------------------
//
// ----------------------------------------------
WorldLayer World::getLayerByName(const char* name) {
	char current[MAX_NAME_LENGTH];
	for ( uint32 i = 0; i < m_Counter; ++i ) {
		getName(i+1,current);
		if ( strcmp(name,current) == 0 ) {
			return i+1;
		}
	}
	LOGC(logERROR,"World") << "layer not found ";
	return 0;
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::activateLayerByName(const char* name) {
	WorldLayer wl = getLayerByName(name);
	if ( wl != 0 ) {
		activateLayer(wl);
	}
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::deactivateLayerByName(const char* name) {
	WorldLayer wl = getLayerByName(name);
	if ( wl != 0 ) {
		deactivateLayer(wl);
	}
}
// ----------------------------------------------
//
// ----------------------------------------------
void World::activateLayer(const WorldLayer& layer) {
	uint32 idx = (layer - 1) * 3 + 1;
	LOGC(logINFO,"World") << "activating layer " << layer << " - offset " << idx;
	uint32* data = (m_Data + idx);
	*data = 1;
	m_Dirty = true;
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::setMask(WorldLayer layer,WorldMask wm) {
	uint32 idx = (layer-1)*3;
	uint32* data = (m_Data + idx + 2);
	*data = wm;
	m_Dirty = true;
	LOGC(logINFO,"World") << "setting mask for layer " << layer << " to " << *data;
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::deactivateLayer(const WorldLayer& layer) {
	uint32 idx = (layer - 1) * 3 + 1;
	LOGC(logINFO,"World") << "deactivating layer " << layer << " - offset " << idx;
	uint32* data = (m_Data + idx);
	*data = 0;
	m_Dirty = true;
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::getActiveLayers(List<WorldLayer>& list) {	
	if ( !m_Dirty ) {
		list.append(m_ActiveLayers);
	}
	else {
		uint32* data = m_Data;
		for ( uint32 i = 0; i < m_Counter;++i ) {
			uint32 layer = *data;
			++data;
			if ( *data == 1 ) {
				list.append(layer);
			}
			data += 2;		
		}
		m_Dirty = false;
	}
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::attachNode(Node* node,const WorldLayer& layer) {
	node->onCreateDevice();
	LOGC(logINFO,"World") << "attaching node " << node->getName() << " to layer " << string::to_string(layer);	
	LayerNode* newLN = new LayerNode();
	newLN->layer = layer;
	newLN->node = node;
	LayerNode* ln = (m_LayerNodes + m_NodeCounter);
	*ln = *newLN;
	++m_NodeCounter;
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::attachNode(Node* node,const char* layerName) {
	node->onCreateDevice();
	LOGC(logINFO,"World") << "attaching node " << node->getName() << " to layer with name " << layerName;
	WorldLayer layer = getLayerByName(layerName);
	if ( layer != 0 ) {
		attachNode(node,layer);		
	}
	else {
		LOGC(logERROR,"World") << "Cannot find matching layer " << layerName;
	}
}

Node* World::getNode(const std::string& name) {
	LOGC(logINFO,"World") << "searching for node '" << name << "'";
	LayerNode* ln = m_LayerNodes;
	for ( size_t j = 0 ; j < m_NodeCounter; ++j ) {
		LOGC(logINFO,"World") << "checking node '" << ln->node->getName() << "'";
		if ( ln->node->getName() == name ) {
			LOGC(logINFO,"World") << "found node";
			return ln->node;
		}
		++ln;
	}
	LOGC(logERROR,"World") << "node not found";
	return 0;
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::getActiveNodes(List<Node*>& list,WorldMask wm) {
    List<WorldLayer> layers;
    getActiveLayers(layers);	
	uint32* data = m_Data;
	for ( uint32 i = 0; i < m_Counter;++i ) {
		uint32 layer = *data;
		++data;
		if ( *data == 1 ) {			
			++data;
			if ( wm == WL_NONE || *data == wm ) {	
				LayerNode* ln = m_LayerNodes;
				for ( size_t j = 0 ; j < m_NodeCounter; ++j ) {
					if ( ln->node->isActive() && ln->layer == layer ) {
						list.append(ln->node);
					}
					++ln;
				}
			}
			++data;
		}
		else {
			data += 2;
		}
		
    }
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::update(float elapsed) {
	LayerNode* ln = m_LayerNodes;
	for ( size_t j = 0 ; j < m_NodeCounter; ++j ) {
		if ( ln->node->isActive() ) {
			ln->node->update(elapsed);
		}
		++ln;
	}		
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::getActiveNodes(List<Node*>& list,std::vector<WorldLayer>& layers,WorldMask wm) {
	uint32* data = m_Data;
	for ( size_t i = 0; i < layers.size(); ++i) {
		WorldLayer layer = *data;
		++data;
		if ( *data == 1 ) {
			++data;
			if ( wm == *data ) {
				LayerNode* ln = m_LayerNodes;
				for ( size_t j = 0 ; j < m_NodeCounter; ++j ) {
					if ( ln->node->isActive() && ln->layer == layer ) {
						list.append(ln->node);
					}
					++ln;
				}				
			}
			++data;
		}
		else {
			data += 2;
		}
	}
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::initializeNodes(ResManager& resourceManager) {
	uint32* data = m_Data;
	LOGC(logINFO,"World") << "calling init on all nodes";
	for ( size_t i = 0; i < m_Counter; ++i) {
		WorldLayer wl = *data;
		LayerNode* ln = m_LayerNodes;
		for ( size_t j = 0 ; j < m_NodeCounter; ++j ) {
			if ( ln->layer == wl ) {
				ln->node->init();
			}
			++ln;
		}		
		data += 3;
	}
	LOGC(logINFO,"World") << "calling loadContent on all nodes";
	data = m_Data;
	for ( size_t i = 0; i < m_Counter; ++i) {
		WorldLayer wl = *data;
		LayerNode* ln = m_LayerNodes;
		for ( size_t j = 0 ; j < m_NodeCounter; ++j ) {			
			if ( ln->layer == wl ) {				
				ln->node->loadContent(resourceManager);
			}
			++ln;
		}	
		data += 3;
	}
}

// ----------------------------------------------
//
// ----------------------------------------------
void World::debug() {
	uint32* data = m_Data;
	char ret[MAX_NAME_LENGTH];
	for ( uint32 i = 0; i < m_Counter;++i ) {
		uint32 layer = *data;
		++data;
		getName(layer,ret);
		if ( *data == 1 ) {
			++data;
			LOGC(logINFO,"World") << "layer " << layer << " / " << ret << " / active / " << *data;
		}
		else {
			++data;
			LOGC(logINFO,"World") << "layer " << layer << " / " << ret << " / inactive / " << *data;
		}
		LayerNode* ln = m_LayerNodes;
		for ( size_t j = 0 ; j < m_NodeCounter; ++j ) {
			if ( ln->layer == layer ) {
				if ( ln->node->isActive() ) {
					LOGC(logINFO,"World") << "-> " << ln->node->getName()  << "  / active";
				}
				else {
					LOGC(logINFO,"World") << "-> " << ln->node->getName()  << "  / inactive";
				}
			}
			++ln;			
		}
		++data;
	}
}

}
